//
//  Boy.m
//  NSRunTimeDemo
//
//  Created by CPZX010 on 16/9/23.
//  Copyright © 2016年 谢昆鹏. All rights reserved.
//

#import "Boy.h"
#import <objc/message.h>
#import <objc/runtime.h>


@implementation Boy

#pragma mark    KVC方式
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    if ([key isEqualToString:@"id"]) {
        self.testID = value;
    }
}




#pragma amrk    Runtime方式
 /**
 * 获取某个类中成员变量列表(数组)
 * 这是C语言函数, 有copy要记得释放内存
 *
 * class:获取哪个类的成员变量
 * count: 返回值成员变量数组的长度(需要传入地址)
 * @return (Ivar *), 其实是一个数组, 取出的元素类型是成员变量Ivar
  class_copyIvarList(Class cls, unsined int *outCount);
 */




/**
 * 获取成员变量名
 * @return 成员变量名, C语言字符串
 *
 * tips: 成员变量名称是带下划线的, 通过截取字符串来获取不带下划线的属性名
 
 ivar_getName(Ivar v);
 */



+ (instancetype)runtimeObjectExchangeWithDic:(NSMutableDictionary *)dic {
    id objc = [[self alloc] init];
    unsigned count = 0;
    //获取自身所有的成员变量
    Ivar *invarlist = class_copyIvarList(self, &count);
    

    for (int i = 0 ; i < count; i++) {
        Ivar ivar = invarlist[i];
#pragma mark    一级转换
        //获取成员变量名字(带下划线的)
        NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];
        //将变量名前面下划线截取掉,获取属性名字作为key
        NSString *key = [name substringFromIndex:1];
        //根据key值从字典中取值
        id value = dic[key];
        
#pragma mark    二级转换---字典中包含字典
        //获取成员变量对应值
        NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
        //属性值是字典,而且属性类型不是系统的类型(@"@{}", 起始就是字典类型外表包裹了@""), 需呀转换
        if ([value isKindOfClass:[NSDictionary class]] && ![ivarType containsString:@"NS"]) {
            //直接获取到的成员变量类型是@"类型", 需要通过替换去掉@"":  @"NSObject"--->NSObject
            ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@\"" withString:@""];
            ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
            
            //获取转换后的类
            Class modleClass = NSClassFromString(ivarType);
            //再次创建内建模型--递归
            value = [modleClass runtimeObjectExchangeWithDic:value];
        }
        
#pragma mark    三级转换  字典包含数组包含字典
        

        
        
        
        //KVC赋值
        [objc setValue:value forKey:key];
    }


    free(invarlist);
    
    return objc;
}



#pragma mark   在此类中替换(交换方法实现:在要交换的实现中; 替换:可以替换其他类中方法的实现)Person中的printNUM方法
+ (void)load {
    Class personClass = NSClassFromString(@"Person");
    SEL personPrintSelector = NSSelectorFromString(@"printNUM:");
    SEL selfPrintSelector = NSSelectorFromString(@"newPrintNum:");
    //获取方法
    Method personMethod = class_getInstanceMethod(personClass, personPrintSelector);
    Method selfMethod = class_getInstanceMethod(self, @selector(newPrintNum:));
    //获取方法实现部分
    IMP personIMP = method_getImplementation(personMethod);
    IMP selfIMP = method_getImplementation(selfMethod);
    //获取描述方法的参数的字符串<c语言字符串>
    const char *personType = method_getTypeEncoding(personMethod);
    const char *selfType = method_getTypeEncoding(selfMethod);
    
#pragma mark    理解的比较模糊~
    /*
     作用:在person中注册newPrintNum方法并且指定IMP为当前printNUM的IMP
     执行次步作用:交换方法(但是personClass中并没有selfPrintSelector这个方法, 有的是原先的方法实现和参数标识)变相的相当于regist或者add这个selfPrintSelector方法(并且IMP为printNUM的IMP);
     */
    class_replaceMethod(personClass, selfPrintSelector, personIMP, personType);
    /*
     作用:将原先personClass中printNUM方法的IMP重新指向为newPrintNumIMP从而实现了replaceMethod的过程
     */
    class_replaceMethod(personClass, personPrintSelector, selfIMP, selfType);
    
    
    
    
    
    
    
    
    
    
    


}

- (void)newPrintNum:(NSInteger)num {
    if (num < 100) {
        NSLog(@"当前的数不超过100, 它的值为%ld.", (long)num);
    }else {
        NSLog(@"当前的数超过100, 它的值为%ld.", (long)num);
    }
}





@end
